﻿using Nemerle.Collections;
using Nemerle.Text;
using Nemerle.Utility;

using System;
using System.Collections.Generic;
using System.Linq;

namespace Nemerle2.Compiler
{
  // From C# spec:
  // 17.1.3 Attribute parameter types
  // The types of positional and named parameters for an attribute class are limited to the attribute parameter types, which are:
  // •	One of the following types: bool, byte, char, double, float, int, long, sbyte, short, string, uint, ulong, ushort.
  // •	The type object.
  // •	The type System.Type.
  // •	An enum type, provided it has public accessibility and the types in which it is nested (if any) also have public accessibility (§17.2).
  // •	Single-dimensional arrays of the above types.

  /// <summary>
  /// Обертка для значений параметров атрибутов.
  /// </summary>
  public variant CustomAttributeValue
  {
    | None
    | Object  { Value  : object }
    | Bool    { Value  : bool }
    | String  { Value  : string; }
    | Double  { Value  : double; }
    | Float   { Value  : float; }
    | Decimal { Value  : decimal; }
    | Int     { Value  : int; }
    | UInt    { Value  : uint; }
    | Long    { Value  : long; }
    | ULong   { Value  : ulong; }
    | Short   { Value  : short; }
    | UShort  { Value  : ushort; }
    | Byte    { Value  : byte; }
    | SByte   { Value  : sbyte; }
    | Char    { Value  : char; }
    | Guid    { Value  : System.Guid; }
    | Type    { Value  : FixedType; }
    | Enum    { Value  : long; EnumType : FixedType; }
    | Array   { Values : array[CustomAttributeValue]; }

    public ToObject(): object
    {
      match (this)
      {
        | Object(val) | Bool(val)  | String(val) | Double(val) | Float(val) | Decimal(val)
        | Int(val)    | UInt(val)  | Long(val)   | ULong(val)  | Short(val) | UShort(val)
        | Byte(val)   | SByte(val) | Char(val)   | Guid(val)   | Type(val)  | Enum(val, _)  => val
        | Array(val) => val.MapToArray(_.ToObject())
        | None => null
      }
    }

    public override ToString() : string
    {
      match (this)
      {
        | Object(val) | Bool(val)  | String(val) | Double(val) | Float(val) | Decimal(val)
        | Int(val)    | UInt(val)  | Long(val)   | ULong(val)  | Short(val) | UShort(val)
        | Byte(val)   | SByte(val) | Char(val)   | Guid(val)   | Type(val)  => val.ToString()
        | Array(val)    => $"array[..$val]"
        | None => "<no value>"
        | Enum(val, FixedType.Class(ti, []))  => 
          def fields = ti.GetFields(NBindingFlags.Public);
          match (fields.FindObject(f => Convert.ToInt64(f.LiteralValue) == val))
          {
            | null => $"$(ti.Name)($val)"
            | field => ti.Name + "." + field.Name;
          }

        | Enum(_, _) => assert(false, "Invalid enem vakue");
      }
    }
  }
}
